#include "bdecode.hpp"
#include "configs.hpp"
#include <iostream>
#include <omp.h>


namespace BCHDecoder{

  // compute the syndrome polynomial
  void compute_S(GF2EX& S, const vec_GF2& codeword){
    GF2X Si;
    long i, j;
    for(j = 1; j <= 2*BCHt; ++j){
      Si = to_GF2X(0);
      for(i = 0; i <= (BCHn - 1); ++i){
	if (IsOne(codeword[i]))
	  Si += GF2X(i * j, 1);
      }
      S += GF2EX(j-1, to_GF2E(Si));
    }
  }

  // void compute_S(GF2EX& S, const vec_GF2& codeword){
  //   long i, j;
  //   vec_GF2EX v;
  //   GF2X x;
  //   v.SetLength(2*BCHt);
  //   #pragma omp parallel shared(v, codeword) private(i, j, x)
  //   {
  //     #pragma omp for schedule (static)
  //     for(j = 1; j <= 2*BCHt; ++j){
  // 	GF2X Si = to_GF2X(0);
  // 	for(i = 0; i <= (BCHn - 1); ++i){
  // 	  if (IsOne(codeword[i])){
  // 	    x = GF2X(i*j, 1);
  // 	    Si = Si + x;
  // 	//    Si += GF2X(i * j, 1);
  // 	  }
  // 	}
  // 	v(j) = GF2EX(j-1, to_GF2E(Si));
  //     }
  //   }
  //   for(j = 1; j <= 2*BCHt; ++j){
  //     S += v(j);
  //   }
  // }

  // the Euclidean algorithm defined in the text book
  void Euclid(GF2EX& u, GF2EX& v, GF2EX& a, GF2EX& b, long mu, long nu){
    vec_GF2EX rs, vs, us;
    GF2EX qi, ri, ui, vi;
    // rs := [a, b]
    append(rs, a);
    append(rs, b);
    // vs := [0, 1]
    append(vs, GF2EX());
    append(vs, GF2EX(0, 1));
    // us := [1, 0]
    append(us, GF2EX(0, 1));
    append(us, GF2EX());
    long i = 1;
    while (!IsZero(rs(rs.length()))) {
      DivRem(qi, ri, rs(i), rs(i+1));
      append(rs, ri);
      ui = us(i) - qi * us(i+1);
      vi = vs(i) - qi * vs(i+1);
      if ((deg(vi) <= mu) && (deg(ri) <= nu)){ 
	break;
      }
      append(us, ui);
      append(vs, vi);
      i++;
    }
    //    cout << "the  vector rs:" << rs << endl;
    u = ri;
    v = vi;
    if(DEBUG){
      std::cout << "Polynomial u(x): " << u << std::endl;
      std::cout << "Polynomial v(x): " << v << std::endl;
    }
  }

  // BCH time-domain decoding algorithm
  void decode(vec_GF2& C, const vec_GF2& codeword){
    vec_GF2 E;
    C.kill();
    C.SetLength(BCHn); E.SetLength(BCHn);
    GF2E::init(BCHpol);
    GF2EX S;
    compute_S(S, codeword);
    if (IsZero(S)){
#ifdef VERBOSE
      std::cout << "No error occurs in the codeword" << std::endl;
#endif
      C = codeword;
    }
    else{
      GF2EX u, v, delta;
      GF2EX a = GF2EX(2*BCHt, 1);
      GF2EX b = S;
      GF2E zero;
      Euclid(u, v, a, b, BCHt, BCHt - 1);
      GF2E v0 = eval(v, zero);
      if (IsZero(v0)){
	C = codeword;
	std::cout << "Warning: more than " << BCHt << " errors occur!" << std::endl;
	return;
      }
      div(delta, v, v0);
      for(long i = 0; i <= (BCHn - 1); ++i){
	if (IsZero(eval(delta, to_GF2E(GF2X((-i + BCHn), 1))  ))){
	  E(i+1) = to_GF2(1);
	}
	else{
	  E(i+1) = to_GF2(0);
	}
	C(i+1) = codeword(i+1) + E(i+1);
      }
    }
    if(DEBUG)
      std::cout << "Polynomial S(x): " << S << std::endl;
  }



}
